﻿using System;
using System.ComponentModel;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;
using System.Reflection;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

using LowLevelGraphics.Filter;
using LowLevelGraphics.Types;

namespace LowLevelGraphics
{
    //TODO: many of this functions could need a rectangle specification (ROI = region of interest)
    //or some other parameters, i'm not sure about other parameters yet...
    //if the rectangle region is used, this class should contain almost no static functions anymore.

    //if we ALSO Use an UnsafeBitmap for each Function, we should turn this into a normal class
    /// <summary>
    /// 
    /// </summary>
    public class BitmapFunctions : IDisposable
    {
        UnsafeBitmap m_Bitmap = null;

        private int m_nHeight = -1;
        private int m_nWidth = -1;

        /// <summary>
        /// primary constructor that uses an unsafe bitmap
        /// </summary>
        /// <param name="_bitmap"></param>
        public BitmapFunctions(UnsafeBitmap _bitmap)
        {
            m_Bitmap = _bitmap;
            m_nHeight = m_Bitmap.Height;
            m_nWidth = m_Bitmap.Width;
        }

        /// <summary>
        /// Adds a watermark to an image
        /// </summary>
        /// <param name="Image">image to add the watermark to</param>
        /// <param name="WatermarkImage">Watermark image</param>
        /// <param name="Opacity">Opacity of the watermark (1.0 to 0.0 with 1 being completely visible and 0 being invisible)</param>
        /// <param name="X">X position in pixels for the watermark</param>
        /// <param name="Y">Y position in pixels for the watermark</param>
        /// <param name="KeyColor">Transparent color used in watermark image, set to null if not used</param>
        /// <returns>The results in the form of a bitmap object</returns>
        public static Bitmap Watermark(UnsafeBitmap Image, UnsafeBitmap WatermarkImage, float Opacity, int X, int Y, Color KeyColor)
        {
            UnsafeBitmap TempBitmap = Image;

            Bitmap NewBitmap = new Bitmap(TempBitmap.Width, TempBitmap.Height);
            Graphics NewGraphics = Graphics.FromImage(NewBitmap);

            TempBitmap.UnlockImage();
            NewGraphics.DrawImage(TempBitmap.InternalBitmap, new Rectangle(0, 0, TempBitmap.Width, TempBitmap.Height), 0, 0, TempBitmap.Width, TempBitmap.Height, GraphicsUnit.Pixel);
            TempBitmap.LockImage();

            float[][] FloatColorMatrix ={
                    new float[] {1, 0, 0, 0, 0},
                    new float[] {0, 1, 0, 0, 0},
                    new float[] {0, 0, 1, 0, 0},
                    new float[] {0, 0, 0, Opacity, 0},
                    new float[] {0, 0, 0, 0, 1}
                };

            ColorMatrix NewColorMatrix = new ColorMatrix(FloatColorMatrix);
            ImageAttributes Attributes = new ImageAttributes();
            Attributes.SetColorMatrix(NewColorMatrix);
            if (KeyColor != null)
            {
                Attributes.SetColorKey(KeyColor, KeyColor);
            }

            WatermarkImage.UnlockImage();
            NewGraphics.DrawImage(WatermarkImage.InternalBitmap, new Rectangle(X, Y, X + TempBitmap.Width, Y + TempBitmap.Height), 0, 0, TempBitmap.Width, TempBitmap.Height, GraphicsUnit.Pixel, Attributes);
            WatermarkImage.LockImage();
            Attributes.Dispose();
            NewGraphics.Dispose();
            return NewBitmap;
        }

        public struct ImageCentroid
        {
            public float Row;
            public float Column;
            public float Area; 
        }

        //private static void DetermineBoundingBox()
        //{
        //    UnsafeBitmap bitmap = MainFormSingleton.ActivePictureBox.BackgroundImage;
        //    new BlackWhitePerimeter().Execute(bitmap);
        //    int nWidth = bitmap.Width;
        //    int nHeight = bitmap.Height;

        //    int x = 0;
        //    int y = 0;
        //    List<Point> aPoint = new List<Point>();
        //    for (y = 0; y < nHeight; y++)
        //    {
        //        for (x = 0; x < nWidth; x++)
        //        {
        //            if (bitmap.GetPixel(x, y) == Definitions.White)
        //            {
        //                aPoint.Add(new Point(x, y));
        //            }
        //        }
        //    }
        //    bitmap.UnlockImage();
        //    BoundingBoxEstimator blub = new BoundingBoxEstimator(aPoint);
        //    blub.Estimate(bitmap);

        //    blub.Draw(Pens.Blue, bitmap.InternalBitmap);
        //}

        //private static void SkinFilter()
        //{
        //    //--------------------------------------------------------------------------
        //    //Skin detection:
        //    UnsafeBitmap bitmap = MainFormSingleton.ActivePictureBox.BackgroundImage;
        //    int nHeight = bitmap.Height;
        //    int nWidth = bitmap.Width;

        //    for (int y = 0; y < nHeight; y++)
        //    {
        //        for (int x = 0; x < nWidth; x++)
        //        {
        //            Color color = bitmap.GetPixel(x, y);
        //            int nSum = color.R + color.G + color.B + 1;

        //            YCbCr ycbcr = new YCbCr(color);

        //            double newVariable = (ycbcr.Cb + 0.6 * ycbcr.Cr);
        //            if (
        //                ycbcr.Y > 80
        //                && ycbcr.Cb > -40 && ycbcr.Cb < -10
        //                && ycbcr.Cr > 17 && ycbcr.Cr < 45
        //                //(137 < ycbcr.Cr && ycbcr.Cr < 177)
        //                // (77 < ycbcr.Cb && ycbcr.Cb < 127)
        //                //&& (190 < newVariable && newVariable < 215)
        //                )
        //            {
        //                bitmap.SetPixel(x, y, Definitions.White);
        //            }
        //            else
        //            {
        //                bitmap.SetPixel(x, y, Definitions.Black);
        //            }
        //        }
        //    }
        //    bitmap.Dispose();
        //}

        ///// <summary>
        ///// 
        ///// </summary>
        //private static void PerPixelFilterWhiteBigger200()
        //{
        //    new PerPixelFilter(new PerPixelFilter.PerPixelFilterDelegate(
        //        delegate(Color _color)
        //        {
        //            if (_color.R > 200)
        //            {
        //                _color = Definitions.White;
        //            }
        //            return _color;
        //        })).Execute(MainFormSingleton.ActivePictureBox.BackgroundImage).UnlockImage();
        //}


        //private static void Unknown()
        //{
        //    Bitmap _bitmap = (Bitmap)MainFormSingleton.ActivePictureBox.BackgroundImage;
        //    UnsafeBitmap bitmap = new UnsafeBitmap(_bitmap);
        //    List<float> aLum = new List<float>();
        //    List<float> aLumExtrema = new List<float>();
        //    List<float> aLumExtremaTemp = new List<float>();
        //    int nHeight = bitmap.Height;
        //    int nWidth = bitmap.Width;
        //    for (int y = 0; y < nHeight; y++)
        //    {
        //        aLum.Clear();
        //        aLum.Add(0);
        //        for (int x = 1; x < nWidth; x++)
        //        {
        //            Color color = bitmap.GetPixel(x, y);
        //            float fValue = 0.299f * color.R + 0.587f * color.G + 0.114f * color.B;
        //            aLum.Add(fValue);
        //            int nValue = (int)fValue;
        //            bitmap.SetPixel(x, y, Color.FromArgb(nValue, nValue, nValue));
        //            float fLumDiff = aLum[x - 1] - aLum[x];
        //            aLumExtrema.Add(fLumDiff);
        //            aLumExtremaTemp.Clear();
        //            int nCount = aLumExtrema.Count >= 7 ? 7 : aLumExtrema.Count;
        //            for (int i = 0; i < nCount; i++)
        //            {
        //                aLumExtremaTemp.Add(aLumExtrema[i]);
        //            }
        //            float fAverageLumExtramaTempThreshold = 0.0f;

        //            if (aLumExtremaTemp.Count > 3)
        //            {
        //                fAverageLumExtramaTempThreshold = (aLumExtremaTemp[1] + aLumExtremaTemp[aLumExtremaTemp.Count - 2]) / 2;
        //            }
        //            else
        //            {
        //                fAverageLumExtramaTempThreshold = aLumExtremaTemp[0];
        //            }
        //            if (fAverageLumExtramaTempThreshold >= 0.01f)
        //            {
        //                bitmap.SetPixel(x, y, Color.Black);
        //            }
        //            else
        //            {
        //                bitmap.SetPixel(x, y, Color.White);
        //            }
        //        }
        //    }
        //    bitmap.Dispose();
        //}

        public static void FastConvolution(Image _image)
        {
            //Fast convolution:
            UnsafeBitmap bitmap = _image;
            int nHeight = bitmap.Height;
            int nWidth = bitmap.Width;

            UnsafeBitmap bitmapCloned = (UnsafeBitmap)bitmap.Clone();
            //kernel
            int[] aKernel = new int[] { 1, 0, 1, 0, 1 };

            //int nFactor = 1;
            int nOffset = 0;
            Color color = Color.Transparent;
            int i = 0;
            int nR = 0;
            int nG = 0;
            int nB = 0;

            int nWeight = 0;
            int nKernelLength = aKernel.Length;
            for (int y = 0; y < nHeight - nKernelLength; y++)
            {
                for (int x = 0; x < nWidth - nKernelLength; x++)
                {
                    Color color1 = bitmapCloned.GetPixel(x, y);
                    for (i = 0; i < nKernelLength; i++)
                    {
                        Color bitmapClonedGetPixelToArgb = bitmapCloned.GetPixel(x + i, y);
                        nR += bitmapClonedGetPixelToArgb.R * aKernel[i];
                        nG += bitmapClonedGetPixelToArgb.G * aKernel[i];
                        nB += bitmapClonedGetPixelToArgb.B * aKernel[i];
                        nWeight = aKernel[i];
                    }
                    for (i = 0; i < nKernelLength; i++)
                    {
                        Color bitmapClonedGetPixelToArgb1 = bitmapCloned.GetPixel(x, y + i);
                        nR += bitmapClonedGetPixelToArgb1.R * aKernel[i];
                        nG += bitmapClonedGetPixelToArgb1.G * aKernel[i];
                        nB += bitmapClonedGetPixelToArgb1.B * aKernel[i];
                        nWeight = aKernel[i];
                    }
                    nR = nR / nKernelLength / 2 / nWeight + nOffset;
                    nG = nG / nKernelLength / 2 / nWeight + nOffset;
                    nB = nB / nKernelLength / 2 / nWeight + nOffset;
                    bitmap.SetPixel(x, y, Color.FromArgb(nR, nG, nB));
                }
            }

            bitmap.Dispose();

        }


        /// <summary>
        /// To find the centroid of an image, the image first has to be binarized. 
        /// The centroid program will then calculate the centroid based on where the majority of the black pixels are located.
        /// If you have a completely black 20 x 20 BMP, then the centroid would be located in the exact center of that square. 
        /// However, if you have an image like the one below, 
        /// the centroid would be located in the center of the black square located in the top left corner.      
        /// </summary>
        /// <param name="originalImage">The original image.</param>
        public static ImageCentroid Centroid(UnsafeBitmap originalImage)
        {
            int nHeight = originalImage.Height;
            int nWidth = originalImage.Width;

            ImageCentroid imageCentroid = new ImageCentroid();
            int r = 0;
            int c = 0;
            for (r = 0; r <= originalImage.Height - 1; r++)
            {
                for (c = 0; c <= originalImage.Width - 1; c++)
                {
                    if (originalImage[c, r] == Definitions.Black)
                    {
                        imageCentroid.Row = imageCentroid.Row + (originalImage.Height - 1) - r;
                        imageCentroid.Column = imageCentroid.Column + c;
                        imageCentroid.Area = imageCentroid.Area + 1.0f;
                    }
                }
            }
            return imageCentroid;
        }

        /// <summary>
        /// Measures the noise (Assume mean is zero)
        /// </summary>
        /// <param name="_bitmap">The _bitmap.</param>
        public static void MeasureNoise(UnsafeBitmap _bitmap)
        {
            int i, j, k;
            float x, y, z;
            double mean, sd;

            int nWidth = _bitmap.Width;
            int nHeight = _bitmap.Height;

            k = 0;
            x = y = 0.0f;
            for (i = 0; i < nHeight; i++)
            {
                for (j = 0; j < nWidth; j++)
                {
                    x += (float)_bitmap[i, j].ToArgb();
                    y += (float)_bitmap[i, j].ToArgb() * (float)_bitmap[i, j].ToArgb();
                    k += 1;
                }
            }

            sd = (double)(y - x * x / (float)k) / (float)(k - 1);
            mean = (double)(x / (float)k);
            sd = Math.Sqrt(sd);
            Debug.WriteLine(string.Format("Image mean is {0} Standard deviation is {1}\n", mean, sd));
            x = y = z = 0.0f;
            for (j = 0; j < nHeight; j++)
            {
                for (i = 0; i < nWidth; i++)
                {
                    z = (float)(_bitmap[i, j].ToArgb() - mean);
                    x += z;
                    y += z * z;
                }
            }
            sd = (double)(y - x * x / (float)k) / (float)(k - 1);
            mean = (double)(x / (float)k);
            sd = Math.Sqrt(sd);
            Debug.WriteLine(string.Format("Noise mean is {0} Standard deviation is: {1}", mean, sd));
        }

        /// <summary>
        /// Copies the specified _bitmap from.
        /// see also http://msdn.microsoft.com/de-de/library/system.drawing.imaging.bitmapdata.aspx
        /// </summary>
        /// <param name="_bitmapFrom">The _bitmap from.</param>
        /// <param name="_bitmapTo">The _bitmap to.</param>
        /// <returns></returns>
        public static bool CopyUnsafe(UnsafeBitmap _bitmapFrom, UnsafeBitmap _bitmapTo)
        {
            //To be able to copy, the bitmaps have to be of the same size
            if (_bitmapFrom.Size != _bitmapTo.Size)
            {
                return false;
            }
            int bytes = _bitmapFrom.BitmapData.Stride * _bitmapFrom.Height;
            byte[] rgbValues = new byte[bytes];

            IntPtr ptr = _bitmapFrom.BitmapData.Scan0;

            // Copy the RGB values into the array.
            Marshal.Copy(ptr, rgbValues, 0, bytes);

            // Copy the RGB values back to the bitmap
            ptr = _bitmapTo.BitmapData.Scan0;
            Marshal.Copy(rgbValues, 0, ptr, bytes);

            return true;
        }

        /// <summary>
        /// Copies the specified _bitmap from.
        /// </summary>
        /// <param name="_bitmapFrom">The _bitmap from.</param>
        /// <param name="_bitmapTo">The _bitmap to.</param>
        /// <returns></returns>
        public static bool Copy(UnsafeBitmap _bitmapFrom, UnsafeBitmap _bitmapTo)
        {
            if (_bitmapFrom.Size != _bitmapTo.Size) return false;
            int x = 0;
            int y = 0;
            int nWidth = _bitmapFrom.Width;
            int nHeight = _bitmapFrom.Height;
            for (y = 0; y < nHeight; y++)
            {
                for (x = 0; x < nWidth; x++)
                {
                    _bitmapTo.SetPixel(x, y, _bitmapFrom.GetPixel(x, y));
                }
            }
            return true;
        }

        /// <summary>
        /// Gets the Colorpalette from set unsafebitmap
        /// </summary>
        /// <param name="_nColors"></param>
        /// <returns></returns>
        public ColorPalette GetColorPalette(int _nColors)
        {
            // Assume monochrome image.
            PixelFormat bitscolordepth = PixelFormat.Format1bppIndexed;
            Bitmap bitmap = this.m_Bitmap.InternalBitmap;
            ColorPalette palette; // The Palette we are stealing
            // Determine number of colors.
            if (_nColors > 2)
                bitscolordepth = PixelFormat.Format4bppIndexed;
            if (_nColors > 16)
                bitscolordepth = PixelFormat.Format8bppIndexed;
            // Make a new Bitmap object to get its Palette.
            bitmap = new Bitmap(1, 1, bitscolordepth);
            palette = bitmap.Palette; // Grab the palette
            return palette; // Send the palette back
        }

        /// <summary>
        /// Return unsafebitmap
        /// </summary>
        public UnsafeBitmap Bitmap
        {
            get { return m_Bitmap; }
        }

        /// <summary>
        /// Creates the average bitmap.
        /// </summary>
        /// <returns></returns>
        public static UnsafeBitmap CreateAverageBitmap(List<UnsafeBitmap> _aBitmap)
        {
            UnsafeBitmap bitmapAverage = new UnsafeBitmap(_aBitmap[0].Width, _aBitmap[0].Height);
            int nHeight = _aBitmap[0].Height;
            int nWidth = _aBitmap[0].Width;
            int nValue = 0;
            for (int y = 0; y < nHeight; y++)
            {
                for (int x = 0; x < nWidth; x++)
                {
                    foreach (UnsafeBitmap bitmap in _aBitmap)
                    {
                        nValue += bitmap.GetPixel(x, y).R;
                    }
                    nValue /= _aBitmap.Count;
                    bitmapAverage.SetPixel(x, y, Color.FromArgb(nValue, nValue, nValue));
                }
            }
            return bitmapAverage;
        }

        /// <summary>
        /// Creates the average gray bitmap from file list.
        /// </summary>
        /// <param name="_aFile">The _a file.</param>
        /// <returns></returns>
        public static UnsafeBitmap CreateAverageGrayBitmapFromFileList(string[] _aFile)
        {
            return CreateAverageGrayBitmapFromFileList(_aFile, -1, -1);
        }

        /// <summary>
        /// Creates the average gray bitmap from file list.
        /// </summary>
        /// <param name="aFile">A file.</param>
        /// <returns></returns>
        public static UnsafeBitmap CreateAverageGrayBitmapFromFileList(string[] aFile, int nFrom, int nTo)
        {
            List<UnsafeBitmap> aBitmap = new List<UnsafeBitmap>();
            foreach (string sFile in aFile)
            {
                UnsafeBitmap bitmap = UnsafeBitmap.FromFile(sFile);
                aBitmap.Add(bitmap);
            }

            if (nFrom == -1 && nFrom == -1)
            {
                nFrom = 0;
                nTo = aBitmap.Count;
            }

            UnsafeBitmap bitmapAverage = new UnsafeBitmap(aBitmap[0].Width, aBitmap[0].Height);
            int nHeight = aBitmap[0].Height;
            int nWidth = aBitmap[0].Width;
            int nValue = 0;
            for (int y = 0; y < nHeight; y++)
            {
                for (int x = 0; x < nWidth; x++)
                {
                    for (int i = nFrom; i < nTo; i++)
                    {
                        UnsafeBitmap bitmap = aBitmap[i];
                        nValue += bitmap.GetPixel(x, y).R;
                    }
                    nValue /= aBitmap.Count;
                    bitmapAverage.SetPixel(x, y, Color.FromArgb(nValue, nValue, nValue));
                }
            }
            return bitmapAverage;
        }

        /// <summary>
        /// Gets the color pixel rectangle.
        /// </summary>
        /// <param name="_rectangle">The _rectangle.</param>
        /// <returns></returns>
        public List<ColorPixel<Point>> GetColorPixelRectangle(Rectangle _rectangle)
        {
            List<ColorPixel<Point>> aList = new List<ColorPixel<Point>>();
            for (int y = 0; y < m_nWidth; y++)
            {
                for (int x = 0; x < m_nHeight; y++)
                {
                    Color color = m_Bitmap.GetPixel(x, y);
                    aList.Add(new ColorPixel<Point>(color, new Point(x, y)));
                }
            }
            return aList;
        }

        /// <summary>
        /// Gets the color pixel line.
        /// </summary>
        /// <param name="_nY">The _n Y.</param>
        /// <returns></returns>
        public ColorPixel<int>[] GetColorPixelLine(int _nY)
        {
            ColorPixel<int>[] aList = new ColorPixel<int>[m_nWidth];
            for (int x = 0; x < m_nWidth; x++)
            {
                Color color = m_Bitmap.GetPixel(x, _nY);
                aList[x] = new ColorPixel<int>(color, new Point(x, _nY), x);
            }
            return aList;
        }

        /// <summary>
        /// Gets the color pixel column.
        /// </summary>
        /// <param name="_nX">The _n X.</param>
        /// <returns></returns>
        public ColorPixel<int>[] GetColorPixelColumn(int _nX)
        {
            ColorPixel<int>[] aList = new ColorPixel<int>[m_nWidth];
            for (int y = 0; y < m_Bitmap.Width; y++)
            {
                Color color = m_Bitmap.GetPixel(_nX, y);
                aList[y] = new ColorPixel<int>(color, new Point(_nX, y), y);
            }
            return aList;
        }

        //TODO: BitmapFunctions AverageOptical Intensity: verify if this is correct
        /// <summary>
        /// same as IntegratedOpticalIntensity but divided by area...
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public int AverageOpticalIntensity()
        {
            int nHeight = m_Bitmap.Height;
            int nWidth = m_Bitmap.Width;
            int nSum = 0;
            for (int y = 0; y < nHeight; y++)
            {
                for (int x = 0; x < nWidth; x++)
                {
                    nSum += m_Bitmap.GetGrayPixelValue(x, y);
                }
            }
            int nResult = nSum / nWidth / nHeight;
            return nResult;
        }

        /// <summary>
        /// Replaces given Colors in Bitmap
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <param name="_aColorDict"></param>
        public void ReplaceColors(Dictionary<Color, Color> _aColorDict)
        {
            for (int y = 0; y < m_nHeight; y++)
            {
                for (int x = 0; x < m_nWidth; x++)
                {
                    Color color = m_Bitmap.GetPixel(x, y);
                    if (_aColorDict.ContainsKey(color))
                    {
                        m_Bitmap.SetPixel(x, y, _aColorDict[color]);
                    }
                }
            }
        }

        /// <summary>
        /// Replace color
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <param name="_processing"></param>
        public void ReplaceColor(ColorProcessing _processing, Color _colorReplace)
        {
            for (int y = 0; y < m_nHeight; y++)
            {
                for (int x = 0; x < m_nWidth; x++)
                {
                    Color color = m_Bitmap.GetPixel(x, y);
                    if (_processing(m_Bitmap, x, y, color))
                    {
                        m_Bitmap.SetPixel(x, y, _colorReplace);
                    }
                }
            }
        }

        /// <summary>
        /// Compute the sum operation to the image with a factor
        /// </summary>
        /// <param name="_dDestiny">The _d destiny.</param>
        /// <param name="_dFactor">The _d factor.</param>
        /// <returns></returns>
        public double[,] SumBitmap(double[,] _dDestiny, double _dFactor)
        {
            int nfil = _dDestiny.GetLength(0);
            int ncol = _dDestiny.GetLength(1);

            for (int y = 0; y < ncol; y++)
            {
                for (int x = 0; x < nfil; x++)
                {
                    Color color = m_Bitmap.GetPixel(x, y);
                    _dDestiny[x, y] += _dFactor * ColorFunctions.Gray(color);
                }
            }
            return _dDestiny;
        }

        /// <summary>
        /// Get pointer to line start
        /// </summary>
        /// <param name="_bitmapData">The _bitmap data.</param>
        /// <param name="_nLineNumber">The _n line number.</param>
        /// <returns></returns>
        unsafe public static byte* GetPointerToLineStart(BitmapData _bitmapData, int _nLineNumber)
        {
            return ((byte*)_bitmapData.Scan0) + _bitmapData.Stride * _nLineNumber;
        }

        /// <summary>
        /// Get N bits
        /// </summary>
        /// <param name="ptr">The PTR.</param>
        /// <param name="_nNumBits">The _n num bits.</param>
        /// <returns></returns>
        unsafe public static int GetNBits(byte* ptr, int _nNumBits)
        {
            if (_nNumBits == 3)
            {
                return *(ptr + 2) * 65536 + *(ptr + 1) * 256 + *(ptr + 0);
            }
            else if (_nNumBits == 4)
            {
                return *(ptr + 3) * 16777216 + *(ptr + 2) * 65536 + *(ptr + 1) * 256 + *(ptr + 0);
            }
            return 0;
        }

        /// <summary>
        /// Get color brightnesses
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public static List<float> ColorBrightnesses(UnsafeBitmap _bitmap)
        {
            List<float> aSum = new List<float>();
            int nWidth = _bitmap.Width;
            int nHeight = _bitmap.Height;
            for (int y = 0; y < nHeight; y++)
            {
                for (int x = 0; x < nWidth; x++)
                {
                    Color color = _bitmap.GetPixel(x, y);
                    aSum.Add(color.GetBrightness() * 255);
                }
            }
            aSum.Sort();
            return aSum;
        }

        /// <summary>
        /// Effect
        /// </summary>
        /// <param name="bmpSource"></param>
        /// <param name="angle"></param>
        /// <param name="numSteps"></param>
        /// <returns></returns>
        public static Bitmap FoldOut(Bitmap _bitmapSource, float _fAngle, int _nNumSteps)
        {
            //TODO: make this more common
            Bitmap bitmap = new Bitmap(_bitmapSource.Width * 2, _bitmapSource.Height * 2, _bitmapSource.PixelFormat);
            Graphics g = Graphics.FromImage(bitmap);

            g.DrawImageUnscaled(_bitmapSource, 0, 0);

            g.TranslateTransform(_bitmapSource.Width * 2 - 1, 0);
            g.RotateTransform(90);
            g.DrawImageUnscaled(_bitmapSource, 0, 0);

            g.ResetTransform();
            g.TranslateTransform(_bitmapSource.Width * 2 - 1, _bitmapSource.Width * 2 - 1);
            g.RotateTransform(180);
            g.DrawImageUnscaled(_bitmapSource, 0, 0);

            g.ResetTransform();
            g.TranslateTransform(0, _bitmapSource.Width * 2 - 1);
            g.RotateTransform(270);
            g.DrawImageUnscaled(_bitmapSource, 0, 0);

            return bitmap;
        }

        /// <summary>
        /// Gets the shift.
        /// </summary>
        /// <param name="_bitmap">The _bitmap.</param>
        /// <returns></returns>
        public static int GetShift(Bitmap _bitmap)
        {
            return GetShift(_bitmap, 0, 1, 10);
        }

        /// <summary>
        /// Calculates the correct shift. This is used by dejitter
        /// </summary>
        /// <param name="bmp"></param>
        /// <param name="nY1"></param>
        /// <param name="nY2"></param>
        /// <returns></returns>
        public static int GetShift(Bitmap bmp, int _nY1, int _nY2)
        {
            return GetShift(bmp, _nY1, _nY2, 10);
        }

        /// <summary>
        /// Gets the shift of two lines in a bitmap.
        /// This is important to recalculate the shift of interlaced pictures.
        /// e.g.: odd Lines: First Frame, even Lines: Second Frame, in same Bitmap
        /// </summary>
        /// <param name="bmp"></param>
        /// <param name="nY1"></param>
        /// <param name="nY2"></param>
        /// <returns></returns>
        public static int GetShift(UnsafeBitmap _bitmap, int nY1, int nY2, int _nCount)
        {
            UnsafeBitmap bitmap = _bitmap;

            List<List<Color>> aColorArray = new List<List<Color>>();
            aColorArray.Add(new List<Color>());
            aColorArray.Add(new List<Color>());

            int[] aY = new int[2];
            aY[0] = nY1;
            aY[1] = nY2;

            int nIndex = 0;
            foreach (int y in aY)
            {
                for (int x = 0; x < bitmap.Width; x++)
                {
                    aColorArray[nIndex].Add(bitmap.GetPixel(x, y));
                }
                nIndex++;
            }

            int nElementCount = aColorArray[0].Count;

            string sText = BitmapFunctions.ColorListToString(aColorArray[0], false);
            string sText2 = BitmapFunctions.ColorListToString(aColorArray[1], false);


            bitmap.Dispose();
            return 0;
        }

        /// <summary>
        /// Convert a color list to a string
        /// </summary>
        /// <param name="_aColor"></param>
        /// <returns></returns>
        public static string ColorListToString(List<Color> _aColor)
        {
            return ColorListToString(_aColor, false);
        }

        /// <summary>
        /// Get the standard deviation of pixel values.
        /// </summary>
        /// <param name="_sImageFileName">Name of the image file.</param>
        /// <returns>Standard deviation.</returns>
        public static double GetStdDev(string _sImageFileName)
        {
            double total = 0, totalVariance = 0;
            int count = 0;
            double stdDev = 0;

            // First get all the bytes

            using (Bitmap b = new Bitmap(_sImageFileName))
            {
                BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadOnly, b.PixelFormat);
                int stride = bmData.Stride;
                IntPtr Scan0 = bmData.Scan0;

                unsafe
                {
                    byte* p = (byte*)(void*)Scan0;
                    int nOffset = stride - b.Width * 3;

                    for (int y = 0; y < b.Height; ++y)
                    {
                        for (int x = 0; x < b.Width; ++x)
                        {
                            count++;
                            byte blue = p[0];
                            byte green = p[1];
                            byte red = p[2];

                            int pixelValue = Color.FromArgb(0, red, green, blue).ToArgb();

                            total += pixelValue;
                            double avg = total / count;

                            totalVariance += Math.Pow(pixelValue - avg, 2);
                            stdDev = Math.Sqrt(totalVariance / count);
                            p += 3;
                        }
                        p += nOffset;
                    }
                }
                b.UnlockBits(bmData);
            }
            return stdDev;
        }

        /// <summary>
        /// Sets those pixels into the bitmap.
        /// </summary>
        /// <param name="pixels">The pixels.</param>
        /// <param name="_bitmap">The _bitmap.</param>
        /// <returns></returns>
        public bool SetBitmapPixels(byte[] pixels, Bitmap _bitmap)
        {
            if (pixels.GetLength(0) != _bitmap.Width * _bitmap.Height)
                return false;

            if (_bitmap.PixelFormat != PixelFormat.Format8bppIndexed)
                return false;

            BitmapData d = _bitmap.LockBits(
                new Rectangle(0, 0, _bitmap.Width, _bitmap.Height),
                ImageLockMode.ReadWrite,
                PixelFormat.Format8bppIndexed);

            IntPtr ptr = d.Scan0;

            int nHeight = _bitmap.Height;
            for (int i = 0; i < nHeight; i++)
            {
                IntPtr p = new IntPtr(ptr.ToInt32() + d.Stride * i);
                Marshal.Copy(pixels, i * _bitmap.Width, p, _bitmap.Width);
            }

            _bitmap.UnlockBits(d);

            return true;
        }

        /// <summary>
        /// Create a region from a bitmap
        /// </summary>
        /// <param name="bitmap"></param>
        /// <returns></returns>
        public static Region CreateRegionFromBitmap(Bitmap bitmap)
        {
            if (!(bitmap.PixelFormat == PixelFormat.Format24bppRgb ||
                bitmap.PixelFormat == PixelFormat.Format32bppArgb ||
                bitmap.PixelFormat == PixelFormat.Format32bppRgb))
                return null;// 24bit/32bit

            //Detect Pixelformatsize
            int nChannels = Image.GetPixelFormatSize(bitmap.PixelFormat) / 8;
            int nOffset = 0;
            int nCount = nChannels;

            //Format32bppArgb
            if (bitmap.PixelFormat == PixelFormat.Format32bppArgb)
            {
                nCount = 1;
                nOffset = 3;
            }

            Rectangle rectBitmap = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
            BitmapData data = bitmap.LockBits(rectBitmap,
                ImageLockMode.ReadOnly, bitmap.PixelFormat);
            byte[] aValues = new byte[data.Height * data.Stride];
            System.Runtime.InteropServices.Marshal.Copy(data.Scan0, aValues, 0, data.Height * data.Stride);
            bitmap.UnlockBits(data);

            byte[] aTransparent = new byte[nCount];
            if (nCount == 1)
            {
                aTransparent[0] = 0;
            }
            else
            {
                for (int i = 0; i < nCount; ++i)
                {
                    aTransparent[i] = aValues[i];
                }
            }

            Region region = new Region();
            region.MakeEmpty();

            Rectangle rect = new Rectangle();
            for (int y = 0; y < data.Height; ++y)
            {
                int offsetY = y * data.Stride;
                for (int x = 0; x < data.Width; ++x)
                {
                    for (int c = 0; c < nCount; ++c)
                    {
                        if (aTransparent[c] != aValues[offsetY + x * nChannels + c + nOffset])
                        {
                            rect.X = x;
                            for (++x; x < data.Width; ++x)
                            {
                                for (c = 0; c < nCount; ++c)
                                {
                                    if (aTransparent[c] != aValues[offsetY + x * nChannels + c + nOffset])
                                        goto CONTINUE;
                                }
                                break;
                            CONTINUE:
                                ;
                            }
                            rect.Width = x - rect.X;
                            rect.Y = y;
                            rect.Height = 1;

                            region.Union(rect);
                            break;
                        }
                    }
                }
            }

            return region;
        }

        /// <summary>
        /// Create a region from a bitmap.
        /// Alternative method.
        /// </summary>
        /// <param name="bitmap"></param>
        /// <returns></returns>
        public static Region CreateRegionFromBitmap2(Bitmap bitmap)
        {
            if (!(bitmap.PixelFormat == PixelFormat.Format24bppRgb ||
                bitmap.PixelFormat == PixelFormat.Format32bppArgb ||
                bitmap.PixelFormat == PixelFormat.Format32bppRgb))
                return null;// 24bit/32bit

            //
            int nChannels = Image.GetPixelFormatSize(bitmap.PixelFormat) / 8;
            int nOffset = 0;
            int nCount = nChannels;
            if (bitmap.PixelFormat == PixelFormat.Format32bppArgb)
            {
                //Format32bppArgb
                nCount = 1;
                nOffset = 3;
            }

            Rectangle rectBitmap = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
            BitmapData data = bitmap.LockBits(rectBitmap,
                ImageLockMode.ReadOnly, bitmap.PixelFormat);
            byte[] aValue = new byte[data.Height * data.Stride];
            System.Runtime.InteropServices.Marshal.Copy(data.Scan0, aValue, 0, data.Height * data.Stride);
            bitmap.UnlockBits(data);

            byte[] aTransparent = new byte[nCount];
            if (nCount == 1)
                aTransparent[0] = 0;
            else
            {
                for (int i = 0; i < nCount; ++i)
                    aTransparent[i] = aValue[i];
            }

            //
            GraphicsPath regionPath = new GraphicsPath();

            Rectangle rectangle = new Rectangle();
            for (int y = 0; y < data.Height; ++y)
            {
                int offsetY = y * data.Stride;
                for (int x = 0; x < data.Width; ++x)
                {
                    for (int c = 0; c < nCount; ++c)
                    {
                        if (aTransparent[c] != aValue[offsetY + x * nChannels + c + nOffset])
                        {//
                            rectangle.X = x;//
                            for (++x; x < data.Width; ++x)
                            {
                                for (c = 0; c < nCount; ++c)
                                {
                                    if (aTransparent[c] != aValue[offsetY + x * nChannels + c + nOffset])
                                        goto CONTINUE;	//
                                }
                                break;	//
                            CONTINUE:
                                ;
                            }
                            rectangle.Width = x - rectangle.X;
                            rectangle.Y = y;
                            rectangle.Height = 1;

                            //rect
                            regionPath.AddRectangle(rectangle);
                            break;
                        }
                    }
                }
            }

            //
            return new Region(regionPath);
        }

        /// <summary>
        /// Convert a bitmap to a string
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public static string BitmapToString(Bitmap _bitmap)
        {
            return BitmapToString(_bitmap, false);
        }

        /// <summary>
        /// Convert a bitmap to a string
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <param name="_bSingleComponentToString"></param>
        /// <returns></returns>
        public static string BitmapToString(Bitmap _bitmap, bool _bSingleComponentToString)
        {
            int nWidth = _bitmap.Width;
            int nHeight = _bitmap.Height;
            StringBuilder stringBuilder = new StringBuilder();
            for (int y = 0; y < nHeight; y++)
            {
                for (int x = 0; x < nWidth; x++)
                {
                    Color color = _bitmap.GetPixel(x, y);
                    if (_bSingleComponentToString)
                    {
                        stringBuilder.Append(color);
                    }
                    else
                    {
                        stringBuilder.Append(color.A);
                        stringBuilder.Append(color.R);
                        stringBuilder.Append(color.G);
                        stringBuilder.Append(color.B);
                    }
                }
            }

            return stringBuilder.ToString();
        }

        /// <summary>
        /// Convert a color list to a string
        /// </summary>
        /// <param name="_aColor"></param>
        /// <param name="_bSingleComponentToString"></param>
        /// <returns></returns>
        public static string ColorListToString(List<Color> _aColor, bool _bSingleComponentToString)
        {
            StringBuilder stringBuilder = new StringBuilder();
            for (int i = 0; i < _aColor.Count; i++)
            {
                if (_bSingleComponentToString)
                {
                    stringBuilder.Append(_aColor[i]);
                }
                else
                {
                    stringBuilder.Append(_aColor[i].A);
                    stringBuilder.Append(_aColor[i].R);
                    stringBuilder.Append(_aColor[i].G);
                    stringBuilder.Append(_aColor[i].B);
                }
            }
            return stringBuilder.ToString();
        }

        /// <summary>
        /// Get bytes per pixel
        /// </summary>
        /// <param name="bmp"></param>
        /// <returns></returns>
        public static int GetBytesPerPixel(Bitmap bmp)
        {
            int nBytes = 0;
            switch (bmp.PixelFormat)
            {
                case PixelFormat.Format64bppArgb:
                case PixelFormat.Format64bppPArgb:
                    nBytes = 8;
                    break;

                case PixelFormat.Format48bppRgb:
                    nBytes = 6;
                    break;

                case PixelFormat.Format24bppRgb:
                    nBytes = 3;
                    break;

                case PixelFormat.Format32bppPArgb:
                case PixelFormat.Format32bppRgb:
                case PixelFormat.Format32bppArgb:
                    nBytes = 4;
                    break;

                case PixelFormat.Format8bppIndexed:
                    nBytes = 1;
                    break;

            }

            return nBytes;
        }

        /// <summary>
        /// Compare pics. 
        /// </summary>
        /// <param name="bmp1"></param>
        /// <param name="bmp2"></param>
        /// <returns></returns>
        public static bool Compare(Bitmap _bitmap1, Bitmap _bitmap2)
        {
            return _bitmap1.Equals(_bitmap2);
        }

        /// <summary>
        /// Convert bitmap to an unsafe bytestream
        /// </summary>
        /// <param name="bmp"></param>
        /// <returns></returns>
        public static unsafe byte[] ConvertToByteStreamUnsafe(Bitmap _bitmap)
        {
            BitmapData bData = _bitmap.LockBits(new Rectangle(new Point(), _bitmap.Size),
                ImageLockMode.ReadOnly,
                PixelFormat.Format24bppRgb);

            // number of bytes in the bitmap
            int byteCount = bData.Stride * _bitmap.Height;
            byte[] aBitmapBytes = new byte[byteCount];

            // Copy the locked bytes from memory
            Marshal.Copy(bData.Scan0, aBitmapBytes, 0, byteCount);

            // don't forget to unlock the bitmap!!
            _bitmap.UnlockBits(bData);

            return aBitmapBytes;
        }

        /// <summary>
        /// Convert back from an unsafe byte stream
        /// </summary>
        /// <param name="bmpBytes"></param>
        /// <param name="imageSize"></param>
        /// <returns></returns>
        public static unsafe Bitmap ConvertFromyteStreamUnsafe(byte[] bmpBytes, Size imageSize)
        {
            Bitmap bmp = new Bitmap(imageSize.Width, imageSize.Height);

            BitmapData bData = bmp.LockBits(new Rectangle(new Point(), bmp.Size),
                ImageLockMode.WriteOnly,
                PixelFormat.Format24bppRgb);

            // Copy the bytes to the bitmap object
            Marshal.Copy(bmpBytes, 0, bData.Scan0, bmpBytes.Length);
            bmp.UnlockBits(bData);
            return bmp;
        }

        /// <summary>
        /// Convert a bitmap to 
        /// </summary>
        /// <param name="bmp"></param>
        /// <returns></returns>
        /// <remarks>Bitmap bytes have to be created via a direct memory copy of the bitmap</remarks>
        public static byte[] ConvertToMemoryStream(Bitmap bmp)
        {
            MemoryStream memoryStream = new MemoryStream();
            // Save to memory using the Jpeg format
            bmp.Save(memoryStream, ImageFormat.Jpeg);

            // read to end
            byte[] bmpBytes = memoryStream.GetBuffer();
            bmp.Dispose();
            memoryStream.Close();

            return bmpBytes;
        }

        //Bitmap bytes have to be created using Image.Save()
        /// <summary>
        /// Convert from memory stream
        /// </summary>
        /// <param name="bmpBytes"></param>
        /// <returns></returns>
        public static Image ConvertFromMemoryStream(byte[] bmpBytes)
        {
            MemoryStream ms = new MemoryStream(bmpBytes);
            Image img = Image.FromStream(ms);
            // Do NOT close the stream!

            return img;
        }

        // import these
        // using System.Runtime.Serialization;
        // using System.Runtime.Serialization.Formatters.Binary;
        /// <summary>
        /// serialize bitmap bmp into a memory stream
        /// </summary>
        /// <param name="bmp"></param>
        /// <returns></returns>
        public byte[] SerializeBitmapToMemoryStream(Bitmap bmp)
        {
            // stream to save the bitmap to
            MemoryStream ms = new MemoryStream();
            BinaryFormatter bf = new BinaryFormatter();
            bf.Serialize(ms, bmp);

            // read to end
            byte[] bmpBytes = ms.GetBuffer();
            bmp.Dispose();
            ms.Close();

            return bmpBytes;
        }

        /// <summary>
        /// deserialize bitmap from a memory stream
        /// </summary>
        /// <param name="bmpBytes"></param>
        /// <returns></returns>
        private Bitmap DeserializeBitmapFromMemoryStream(byte[] bmpBytes)
        {
            BinaryFormatter bf = new BinaryFormatter();
            // copy the bytes to the memory
            MemoryStream ms = new MemoryStream(bmpBytes);
            return (Bitmap)bf.Deserialize(ms);
        }

        /// <summary>
        /// Get image handle by reflection
        /// </summary>
        /// <param name="img"></param>
        /// <returns></returns>
        public IntPtr GetImageHandleByReflection(Image img)
        {
            FieldInfo fi = typeof(Image).GetField("nativeImage", BindingFlags.NonPublic | BindingFlags.Instance);
            if (fi == null)
                return IntPtr.Zero;

            return (IntPtr)fi.GetValue(img);
        }

        /// <summary>
        /// Convert bitmap into bytes
        /// </summary>
        /// <returns></returns>
        public byte[] ConvertBitmapToBytes()
        {
            byte[] aByte = (byte[])TypeDescriptor.GetConverter(m_Bitmap.InternalBitmap).ConvertTo(m_Bitmap.InternalBitmap, typeof(byte[]));
            return aByte;
        }

        /// <summary>
        /// Return color count of bitmap
        /// </summary>
        /// <param name="bmp"></param>
        /// <returns></returns>
        public Dictionary<Color, int> ColorCount(Bitmap bmp)
        {
            Dictionary<Color, int> aColorCount = new Dictionary<Color, int>();
            UnsafeBitmap bitmap = new UnsafeBitmap(bmp);
            for (int y = 0; y < bmp.Height; y++)
            {
                for (int x = 0; x < bmp.Width; x++)
                {
                    Color color = bmp.GetPixel(x, y);
                    if (!aColorCount.ContainsKey(color))
                    {
                        aColorCount.Add(color, 1);
                    }
                    else
                    {
                        aColorCount[color]++;
                    }
                }
            }
            bmp.Dispose();
            return aColorCount;
        }

        // Calculate white pixels 
        /// <summary>
        /// Calculates white pixels
        /// </summary>
        /// <param name="image"></param>
        /// <returns></returns>
        public static int CalculateWhitePixels(Bitmap image)
        {
            int count = 0;

            int nWidth = image.Width;
            int nHeight = image.Height;
            // lock difference image 

            BitmapData data = image.LockBits(new Rectangle(0, 0, nWidth, nHeight),
                ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);

            int nOffset = data.Stride - nWidth;

            unsafe
            {
                byte* ptr = (byte*)data.Scan0.ToPointer();

                for (int y = 0; y < nHeight; y++)
                {
                    for (int x = 0; x < nWidth; x++, ptr++)
                    {
                        count += ((*ptr) >> 7);
                    }
                    ptr += nOffset;
                }
            }
            // unlock image 
            image.UnlockBits(data);

            return count;
        }

        /// <summary>
        /// Compares both bitmaps pixel by pixel.
        /// This is only possible if both bitmaps are of the same height.
        /// </summary>
        /// <param name="bmp1">The BMP1.</param>
        /// <param name="bmp2">The BMP2.</param>
        /// <returns></returns>
        public static bool CompareByPixel(Bitmap bmp1, Bitmap bmp2)
        {
            if (bmp1.Width != bmp2.Width || bmp1.Height != bmp2.Height) return false;
            for (int y = 0; y < bmp1.Height; y++)
            {
                for (int x = 0; x < bmp1.Width; x++)
                {
                    if (bmp1.GetPixel(x, y) != bmp2.GetPixel(x, y))
                    {
                        return false;
                    }
                }
            }
            return true;
        }

        /// <summary>
        /// Lets appear a picture standing on a reflecting ground.
        /// </summary>
        /// <param name="source">Image to apply effect to</param>
        /// <param name="height">Height of the reflection (percentage)</param>
        /// <param name="startTransparency">
        ///     transparency the effect starts with
        /// </param>
        /// <param name="BackgroundColor">Color of the ground</param>
        /// <returns>a bitmap with the desired effect</returns>
        private Bitmap ReflectingGround(Image source, byte height,
            byte startTransparency, Color BackgroundColor)
        {
            // height needed for the shadow
            int shadowHeight = Convert.ToInt32(
                Convert.ToDouble(height) / 100 * source.Height);

            // create new bitmap for the generated picture
            Bitmap myBitmap = new Bitmap(source.Width,
                source.Height + shadowHeight);

            Graphics myGraphics = Graphics.FromImage(myBitmap);

            // draw the source image
            myGraphics.DrawImage(source, 0, 0);

            // flip the source image and draw it below the source image
            Image flipped = (Image)source.Clone();
            flipped.RotateFlip(RotateFlipType.RotateNoneFlipY);

            myGraphics.DrawImage(flipped, 0, source.Height);

            // create gradient from semi-transparent to transparent over
            // the flipped copy
            Rectangle gradientRect = new Rectangle(0, source.Height,
                source.Width, shadowHeight);

            // create brush
            LinearGradientBrush myBrush = new LinearGradientBrush(
                gradientRect,
                Color.FromArgb(startTransparency, BackgroundColor),
                Color.FromArgb(255, BackgroundColor),
                LinearGradientMode.Vertical);

            // draw gradient
            myGraphics.FillRectangle(myBrush, gradientRect);

            // return created bitmap
            return myBitmap;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="_bitmap1"></param>
        /// <param name="_bitmap2"></param>
        /// <returns></returns>
        public static bool CompareByPixelUnsafe(UnsafeBitmap _bitmap1, UnsafeBitmap _bitmap2)
        {
            int y = 0;
            int x = 0;
            int nWidth = _bitmap1.Width;
            int nHeight = _bitmap1.Height;

            bool bEqual = true;
            for ( y = 0; y < nHeight; y++)
            {
                for (x = 0; x < nWidth; x++)
                {
                    if (_bitmap1.GetPixel(x, y) != _bitmap2.GetPixel(x, y))
                    {
                        bEqual = false;
                        break;
                    }
                }
            }
            return bEqual;
        }

        /// <summary>
        /// Inverts the specified Bitmap.
        /// </summary>
        /// <param name="bmp1">The BMP1.</param>
        /// <param name="bmp2">The BMP2.</param>
        /// <returns></returns>
        public static bool CompareByPixelUnsafe(Bitmap bmp1, Bitmap bmp2)
        {
            if (bmp1.Width != bmp2.Width || bmp1.Height != bmp2.Height) return false;
            // GDI+ still lies to us - the return format is BGR, NOT RGB.
            BitmapData bmData1 = bmp1.LockBits(new Rectangle(0, 0, bmp1.Width, bmp1.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            BitmapData bmData2 = bmp2.LockBits(new Rectangle(0, 0, bmp2.Width, bmp2.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            int stride1 = bmData1.Stride;
            int stride2 = bmData2.Stride;
            System.IntPtr Scan01 = bmData1.Scan0;
            System.IntPtr Scan02 = bmData2.Scan0;

            bool bEqual = true;

            unsafe
            {
                byte* p1 = (byte*)(void*)Scan01;
                byte* p2 = (byte*)(void*)Scan02;

                int nOffset1 = stride1 - bmp1.Width * 3;
                int nOffset2 = stride2 - bmp2.Width * 3;
                int nWidth = bmp1.Width * 3;

                for (int y = 0; y < bmp1.Height; ++y)
                {
                    if (!bEqual)
                    {
                        break;
                    }
                    for (int x = 0; x < nWidth; ++x)
                    {
                        byte r1 = Marshal.ReadByte(Scan01, nOffset1 + 2);
                        byte g1 = Marshal.ReadByte(Scan01, nOffset1 + 1);
                        byte b1 = Marshal.ReadByte(Scan01, nOffset1);

                        byte r2 = Marshal.ReadByte(Scan02, nOffset2 + 2);
                        byte g2 = Marshal.ReadByte(Scan02, nOffset2 + 1);
                        byte b2 = Marshal.ReadByte(Scan02, nOffset2);

                        Color color1 = Color.FromArgb(r1, g1, b1);
                        Color color2 = Color.FromArgb(r2, g2, b2);
                        if (color1 != color2)
                        {
                            bEqual = false;
                            break;
                        }
                    }
                    p1 += nOffset1;
                    p2 += nOffset2;
                }
            }

            bmp1.UnlockBits(bmData1);
            bmp2.UnlockBits(bmData2);
            return bEqual;
        }

        /// <summary>
        /// Resizes a pic
        /// </summary>
        /// <param name="bitmap"></param>
        /// <param name="szSize"></param>
        /// <returns></returns>
        public static Bitmap Resize(Bitmap bitmap, Size szSize)
        {
            return new Bitmap(bitmap, szSize);
        }

        //TODO: resize, keeping aspect ratio
        /// <summary>
        /// resizes a pic by keeping aspect ratio if wanted
        /// </summary>
        /// <param name="bitmap"></param>
        /// <param name="szSize"></param>
        /// <param name="bKeepAspectRatio"></param>
        /// <returns></returns>
        public static Bitmap Resize(Bitmap bitmap, Size szSize, bool bKeepAspectRatio)
        {
            if (bKeepAspectRatio)
            {
                return bitmap;
            }
            return Resize(bitmap, szSize);
        }

        /// <summary>
        /// Resizes the pic by width.
        /// </summary>
        /// <param name="sourceImage">The source image.</param>
        /// <param name="newWidth">The new width.</param>
        /// <returns></returns>
        public static Bitmap Resize(Image sourceImage, double newWidth)
        {
            double sizeFactor = newWidth / sourceImage.Width;
            double newHeigth = sizeFactor * sourceImage.Height;
            Bitmap newImage = new Bitmap((int)newWidth, (int)newHeigth);
            using (Graphics g = Graphics.FromImage(newImage))
            {
                g.InterpolationMode = InterpolationMode.HighQualityBicubic;
                g.DrawImage(sourceImage, new Rectangle(0, 0, (int)newWidth, (int)newHeigth));
            }
            return newImage;
        }

        /// <summary>
        /// Another resizing
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="outputFileName"></param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        public static void Resize(string fileName, string outputFileName, int width, int height)
        {
            //Open the original image
            Image original = Image.FromFile(fileName);

            //Create a bitmap of the correct size.
            Bitmap temp = new Bitmap(width, height, original.PixelFormat);

            //Get a Graphics object from the bitmap.
            Graphics newImage = Graphics.FromImage(temp);

            //Set the quality of the output image.
            newImage.SmoothingMode = SmoothingMode.Default;
            newImage.InterpolationMode = InterpolationMode.Bicubic;

            //Draw the image with the new width/height
            newImage.DrawImage(original, 0, 0, width, height);

            //Save the bitmap
            temp.Save(@"c:\" + outputFileName);

            //Dispose of our objects.
            original.Dispose();
            temp.Dispose();
            newImage.Dispose();
        }

        /// <summary>
        /// And another resizing
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="outputFileName"></param>
        /// <param name="newWidth"></param>
        public static void Resize(string fileName, string outputFileName, int newWidth)
        {
            Image original = Image.FromFile(fileName);

            //Find the aspect ratio between the height and width.
            float aspect = (float)original.Height / (float)original.Width;

            //Calculate the new height using the aspect ratio
            // and the desired new width.
            int newHeight = (int)(newWidth * aspect);

            //Create a bitmap of the correct size.
            Bitmap temp = new Bitmap(newWidth, newHeight, original.PixelFormat);

            //Get a Graphics object from the bitmap.
            Graphics newImage = Graphics.FromImage(temp);

            //Draw the image with the new width/height
            newImage.DrawImage(original, 0, 0, newWidth, newHeight);

            //Save the bitmap
            temp.Save(@"c:\" + outputFileName);

            //Dispose of our objects.
            original.Dispose();
            temp.Dispose();
            newImage.Dispose();
        }

        /// <summary>
        /// Diese Funktion gibt einen bestimmten Ausschnitt aus einem Bild zurück
        /// </summary>
        /// <param name="SourceImage">Das Bild aus dem der Ausschnitt gelesen werden soll</param>
        /// <param name="Region">Die Position, Größe und Breite des Ausschnitts</param>
        public static Bitmap GetPicturePart(Image SourceImage, Rectangle Region)
        {
            Bitmap ImagePart = new Bitmap(Region.Width, Region.Height);
            using (Graphics G = Graphics.FromImage(ImagePart))
            {
                Rectangle TargetRect = new Rectangle(0, 0, Region.Width, Region.Height);
                Rectangle SourceRect = Region;
                G.DrawImage(SourceImage, TargetRect, SourceRect, GraphicsUnit.Pixel);
            }
            return ImagePart;
        }

        /// <summary>
        /// Gets the picture part.
        /// </summary>
        /// <param name="_bitmap">The _bitmap.</param>
        /// <param name="region">The region.</param>
        /// <returns></returns>
        public static UnsafeBitmap GetPicturePart(UnsafeBitmap _bitmap, Rectangle region)
        {
            return null;
        }

        /// <summary>
        /// Crops a pic, by using get picture part
        /// </summary>
        /// <param name="bitmap"></param>
        /// <param name="ptStart"></param>
        /// <param name="szSize"></param>
        /// <returns></returns>
        public static Bitmap Crop(Bitmap bitmap, Point ptStart, Size szSize)
        {
            return GetPicturePart(bitmap, new Rectangle(ptStart, szSize));
        }

        /// <summary>
        /// Automatically detect background and cut (crop) picture by filtering
        /// This will try to detect amounts of pixels that should contain no important
        /// picture information.
        /// Example: 
        /// 
        /// imagine a picture showing a men or women before a white wall.
        /// The white wall extends the pic. Centered in the pic we got the human being.
        /// So the limits will be cut off, left will be only a rectangle showing the human
        /// being. try it to see the effect.
        /// </summary>
        /// <param name="bitmap"></param>
        /// <returns></returns>
        public static Bitmap AutoCrop(Bitmap bitmap)
        {
            EstimateBoundingBox estimateBackground = new EstimateBoundingBox(Color.Transparent);
            estimateBackground.Execute(bitmap);

            List<Range> aLeftRange = estimateBackground.LeftRanges;
            List<Range> aRightRange = estimateBackground.RightRanges;

            aLeftRange.Sort(new RangeComparer(RANGE_COMPARER_MODE.EndX));
            aRightRange.Sort(new RangeComparer(RANGE_COMPARER_MODE.StartX));

            int nLeft = aLeftRange[0].EndX;

            int nRight = aRightRange[aRightRange.Count - 1].StartX;

            Rectangle rectangleCrop = new Rectangle(nLeft, 0, nRight, bitmap.Height - 1);

            //Only to debug
            //Graphics g = Graphics.FromImage(bitmap);
            //g.DrawRectangle(Pens.Red, rectangleCrop);
            //g.Dispose();
            bitmap = Crop(bitmap, rectangleCrop);
            return bitmap;
        }

        /// <summary>
        /// Crops a pic, by specifying a rectangle
        /// </summary>
        /// <param name="bitmap"></param>
        /// <param name="rect"></param>
        /// <returns></returns>
        public static Bitmap Crop(Bitmap bitmap, Rectangle rect)
        {
            return GetPicturePart(bitmap, rect);
        }

        /// <summary>
        /// Rotates a pic
        /// </summary>
        /// <param name="bitmap"></param>
        /// <param name="fAngle"></param>
        /// <returns></returns>
        public Bitmap Rotate(Bitmap bitmap, float fAngle)
        {
            Graphics graphics = Graphics.FromImage(bitmap);
            graphics.RotateTransform(fAngle);
            graphics.Dispose();
            return bitmap;
        }

        /// <summary>
        /// Rotates the image by angle.
        /// </summary>
        /// <param name="oldBitmap">The old bitmap.</param>
        /// <param name="angle">The angle.</param>
        /// <returns></returns>
        public static Bitmap RotateImageByAngle(Image _bitmapOld, float angle)
        {
            var newBitmap = new Bitmap(_bitmapOld.Width, _bitmapOld.Height);
            var graphics = Graphics.FromImage(newBitmap);
            graphics.TranslateTransform((float)_bitmapOld.Width / 2, (float)_bitmapOld.Height / 2);
            graphics.RotateTransform(angle);
            graphics.TranslateTransform(-(float)_bitmapOld.Width / 2, -(float)_bitmapOld.Height / 2);
            graphics.DrawImage(_bitmapOld, new Point(0, 0));
            return newBitmap;
        }

        /// <summary>
        /// Rotates the image by angle.
        /// </summary>
        /// <param name="bmp">The BMP.</param>
        /// <param name="angle">The angle.</param>
        /// <returns></returns>
        public static Bitmap RotateImageByAngle(Bitmap bmp, double angle)
        {
            Graphics g = null;
            Bitmap tmp = new Bitmap(bmp.Width, bmp.Height, PixelFormat.Format32bppRgb);

            tmp.SetResolution(bmp.HorizontalResolution, bmp.VerticalResolution);
            g = Graphics.FromImage(tmp);
            try
            {
                g.FillRectangle(Brushes.White, 0, 0, bmp.Width, bmp.Height);
                g.RotateTransform((float)angle);
                g.DrawImage(bmp, 0, 0);
            }
            finally
            {
                g.Dispose();
            }
            return tmp;
        }

        /// <summary>
        /// Test if we are out of range
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <param name="nX"></param>
        /// <param name="nY"></param>
        /// <returns></returns>
        public static bool BoundLimitsReached(IBitmap _bitmap, int nX, int nY)
        {
            return (nY > _bitmap.Height - 1 || nY < 0 || nX > _bitmap.Width - 1 || nX < 0);
        }

        /// <summary>
        /// Test if we are out of range
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <param name="_ptPoint"></param>
        /// <returns></returns>
        public static bool BoundLimitsReached(IBitmap _bitmap, Point _ptPoint)
        {
            return !(new Rectangle(0, 0, _bitmap.Width, _bitmap.Height).Contains(_ptPoint));
        }

        /// <summary>
        /// Changes the pixelformat of a given bitmap into any of the GDI+ supported formats.
        /// </summary>
        /// <param name="oldBmp">Die Bitmap die verändert werden soll.</param>
        /// <param name="NewFormat">Das neu anzuwendende Pixelformat.</param>
        /// <returns>Die Bitmap mit dem neuen PixelFormat</returns>
        public static Bitmap ChangePixelFormat(Bitmap _bitmapOld, PixelFormat NewFormat)
        {
            return (_bitmapOld.Clone(new Rectangle(0, 0, _bitmapOld.Width, _bitmapOld.Height), NewFormat));
        }

        /// <summary>
        /// see also GetNonIndexedBitmap(Bitmap _bitmap, bool _bForce) 
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public static Bitmap GetNonIndexedBitmap(Bitmap _bitmap)
        {
            return GetNonIndexedBitmap(_bitmap, true);
        }

        /// <summary>
        /// Determine whether this pic is indexed
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public static bool IsIndexed(Bitmap _bitmap)
        {
            bool bIsIndexed = false;
            switch (_bitmap.PixelFormat)
            {
                case PixelFormat.Format1bppIndexed:
                case PixelFormat.Format4bppIndexed:
                case PixelFormat.Format8bppIndexed:
                    bIsIndexed = true;
                    break;
            }
            return bIsIndexed;
        }

        /// <summary>
        /// Returns a non indexed Bitmap from an indexed bitmap,
        /// use this for a e.g gif if you want to use SetPixel on it.
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public static Bitmap GetNonIndexedBitmap(Bitmap _bitmap, bool _bForce)
        {
            switch (_bitmap.PixelFormat)
            {
                case PixelFormat.Undefined:
                case PixelFormat.Format1bppIndexed:
                case PixelFormat.Format4bppIndexed:
                case PixelFormat.Format8bppIndexed:
                case PixelFormat.Format16bppGrayScale:
                case PixelFormat.Format16bppArgb1555:

                    // Create a new BitMap object using original Image instance
                    _bitmap = new Bitmap(_bitmap);
                    break;
            }
            if (_bForce)
            {
                _bitmap = new Bitmap(_bitmap);
            }
            return _bitmap;
        }


        /// <summary>
        /// Changes the Opacity of an Image and returns the new Image
        /// </summary>
        /// <param name="original"></param>
        /// <param name="opacity"></param>
        /// <returns></returns>
        public static Image SetOpacity(Image original, float opacity)
        {
            Bitmap temp = new Bitmap(original.Width, original.Height);
            Graphics graphics = Graphics.FromImage(temp);
            ColorMatrix cm = new ColorMatrix();
            cm.Matrix33 = opacity;

            ImageAttributes ia = new ImageAttributes();
            ia.SetColorMatrix(cm, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
            graphics.DrawImage(original, new Rectangle(0, 0, temp.Width, temp.Height), 0, 0, original.Width, original.Height, GraphicsUnit.Pixel, ia);
            graphics.Dispose();
            return temp;
        }

        #region IDisposable Members

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            m_Bitmap.UnlockImage();
        }

        /// <summary>
        /// Distructor
        /// </summary>
        ~BitmapFunctions()
        {
            Dispose();
        }

        #endregion
    }
}
